#! /usr/bin/env python
#coding=utf-8

import time

class TableBuilder(object):
	def __init__(self, cursor, name):
		self._cursor = cursor
		self._name = name

	def getTableName(self):
		return self._name

	def getCursor(self):
		return self._cursor

	def __check_if_exists(self):
		sqlcmd = "select tbl_name from sqlite_master where type=\"table\" and tbl_name=\"%s\"" % self._name
		self._cursor.execute(sqlcmd)
		for row in self._cursor:
			return True
		return False

	def createTable(self, recreate=True):
		if not recreate and self.__check_if_exists():
			return False

		# drop if exists
		sqlcmd = "drop table if exists %s" % self._name
		self._cursor.execute(sqlcmd)

		# create table
		keys = ("id INTEGER PRIMARY KEY", ) + self.getColumns() + self.getColumnsExtraInfo()

		sqlcmd = "create table %s(%s)" % (self._name, ", ".join(keys))
		#print(sqlcmd)
		self._cursor.execute(sqlcmd)

		return True

	# To be overrided
	def getColumns(self):
		return ()

	# To be overrided
	def getColumnsExtraInfo(self):
		return ()

	def __getSqliteVal(self, obj, k):
		if k in obj:
			val = obj[k]
		else:
			val = ""
		if val is None:
			return "'" + str(val) + "'"
		if type(val) in (tuple, list):
			return str(len(val))
		elif isinstance(val, str):
			return "'" + str(val).replace("'", "") + "'"
		elif isinstance(val, bool):
			return str(int(val))
		elif isinstance(val, int):
			return str(val)
		return "'" + str(val) + "'"

	def __build_sql(self, obj, no_id=False):
		if no_id:
			keys = self.getColumns()
		else:
			keys = ("id", ) + self.getColumns()
		vals = [self.__getSqliteVal(obj, k) for k in keys]
		return "insert into %s (%s) values (%s)" % (self._name, ", ".join(keys), ", ".join(vals))

	def saveToDB(self, obj, no_id=False):
		sqlcmd = self.__build_sql(obj, no_id)
		self._cursor.execute(sqlcmd)
		return sqlcmd

	def addMultiObjToDb(self, objs, cookie=None, preprocessor=None, logFile=None, no_id=False):
		needCommit = False
		total = len(objs)
		script = "BEGIN;\n"
		for idx, obj in enumerate(objs):
			if preprocessor:
				preprocessor(obj, idx, cookie)
			sqlcmd = self.__build_sql(obj, no_id)
			script = script + sqlcmd + ";\n"
			needCommit = True

			if len(script) > 4096:
				script = script + "COMMIT;\n"
				self._cursor.executescript(script)
				if logFile:
					logFile.write(script)
				script = "BEGIN TRANSACTION;\n"
				needCommit = False

		if needCommit:
			script = script + "COMMIT;\n"
			self._cursor.executescript(script)
			if logFile:
				logFile.write(script)

		return total

class ModuleTableBuilder(TableBuilder):
	def __init__(self, cursor):
		super(ModuleTableBuilder, self).__init__(cursor, "modules")

	def getColumns(self):
		return ("object_id", "name", "size", "text_size", "data_size", "oc_size", "others_size", \
			"provided", "public_symbols", "used", "needed", "matched", "duplicated", "unmatched", \
			"deps", "deps_indirect", "deps_total", "depth", "dependedBy", "dependedBy_indirect", "dependedBy_total", "dependedBy_depth")
class DepedenceiesTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "dependencies")

	def getColumns(self):
 		return ("caller_id", "callee_id", "calls")

	def getColumnsExtraInfo(self):
		return ("FOREIGN KEY(caller_id) REFERENCES modules(id)", "FOREIGN KEY(callee_id) REFERENCES modules(id)")

class SymbolsTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "symbols")

	def getColumns(self):
		return ("parent_id", "name", "addr", "bits", "name_type", "section")

	def getColumnsExtraInfo(self):
		return ("FOREIGN KEY(parent_id) REFERENCES modules(id)", )

class SymbolNamesTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "symbol_names")

	def getColumns(self):
		return ("name", "demangle", "count", "calls")

class UndefinedSymbolsTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "undefines")

	def getColumns(self):
		return ("parent_id", "name", "bits", "demangle")

	def getColumnsExtraInfo(self):
		return ("FOREIGN KEY(parent_id) REFERENCES modules(id)", )

class CallsTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "calls")

	def getColumns(self):
		return ("caller_id", "callee_id", "symbol_id", "dependence_id")

	def getColumnsExtraInfo(self):
		return ("FOREIGN KEY(caller_id) REFERENCES modules(id)", "FOREIGN KEY(callee_id) REFERENCES modules(id)", "FOREIGN KEY(symbol_id) REFERENCES symbols(id)", "FOREIGN KEY(dependence_id) REFERENCES dependencies(id)")

class IndirectsTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "indirects")

	def getColumns(self):
		return ("caller_id", "callee_id", "external")

	def getColumnsExtraInfo(self):
		return ("FOREIGN KEY(caller_id) REFERENCES modules(id)", "FOREIGN KEY(callee_id) REFERENCES modules(id)")

import os
import sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
from vmmap import VMMAP_SIZE_KEYS
from vmmap import PROCESS_SIZE_KEYS

class MemoryRecordBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "mem_records")

	def getColumns(self):
		return ("name", "description", "Pages_free", "Pages_active", "Pages_inactive", "Pages_speculative", "Pages_throttled", "Pages_wired_down", "Pages_purgeable", "Translation_faults", "Pages_copy_on_write", "Pages_zero_filled", "Pages_reactivated", "Pages_purged", "File_backed_pages", "Anonymous_pages", "Pages_stored_in_compressor", "Pages_occupied_by_compressor", "Decompressions", "Compressions", "Pageins", "Pageouts", "Swapins", "Swapouts", "processes", "region_types") + PROCESS_SIZE_KEYS

class ProcessTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "processes")

	def getColumns(self):
		return ("record_id", "pid", "ppid", "cpu", "state", "priority", "nice", "vsize", "wq", "region_types", "command") + PROCESS_SIZE_KEYS

	def getColumnsExtraInfo(self):
		return ("FOREIGN KEY(record_id) REFERENCES mem_records(id)", )

class SystemObjectTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "objects")

	def getColumns(self):
		return ("record_id", "name", "processes", "marcho") + VMMAP_SIZE_KEYS

class ProcessObjectTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "objects")

	def getColumns(self):
		return ("name", "process_id", "marcho", "regions") + VMMAP_SIZE_KEYS

class RegionTypeTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "region_types")

	def getColumns(self):
		return ("record_id", "process_id", "name", "regions") + VMMAP_SIZE_KEYS

	def getColumnsExtraInfo(self):
		return ("FOREIGN KEY(record_id) REFERENCES mem_records(id)", "FOREIGN KEY(process_id) REFERENCES processes(id)")

class RegionTableBuilder(TableBuilder):
	def __init__(self, cursor):
		TableBuilder.__init__(self, cursor, "regions")

	def getColumns(self):
		return ("record_id", "process_id", "type_id", "addr", "ugo", "sharemode", "purge", "detail") + VMMAP_SIZE_KEYS

	def getColumnsExtraInfo(self):
		return ("FOREIGN KEY(record_id) REFERENCES mem_records(id)", "FOREIGN KEY(process_id) REFERENCES processes(id)", "FOREIGN KEY(type_id) REFERENCES region_types(id)")

if __name__ == '__main__':
	import sqlite3

	conn = sqlite3.connect("../symdb.db")
	cursor = conn.cursor()

	builder = ModuleTableBuilder(cursor)
	builder.createTable()

	conn.commit()
	cursor.close()
	conn.close()
